import numpy as np

from common.functions import cross_entropy_error, softmax

class Relu:
    def __init__(self):
        self.params,self.grads = [],[]
        self.mask = None 

    def forward(self,x):
        self.mask = (x<=0)
        out = x.copy()
        out[self.mask] = 0

        return out 
    
    def backward(self,dout):
        dout[self.mask] = 0
        dx = dout 
        return dx 


class Sigmoid:
    def __init__(self):
        self.params,self.grads = [],[]
        self.out = None

    def forward(self,x):
        out = 1/(1+np.exp(-x))
        self.out = out 
        return out 
    
    def backward(self,dout):
        dx = dout*(1.0-self.out)*self.out 
        return dx 
    
class Affine:
    def __init__(self,W,b):
        self.params = [W,b]
        self.grads = [np.zeros_like(W),np.zeros_like(b)]
        self.x = None

    def forward(self,x):
        self.x = x
        W,b = self.params
        out = np.dot(x,W) + b
        return out 
    
    def backward(self,dout):
        W,b = self.params

        db = np.sum(dout,axis=0)
        dx = np.dot(dout,W.T)
        dW = np.dot(self.x.T,dout)

        self.grads[0][...] = dW
        self.grads[1][...] = db 

        return dx 
    
class SoftmaxWithLoss:
    def __init__(self):
        self.params,self.grads = [],[]
        self.loss = None 
        self.y = None # softmax输出
        self.t = None # 监督标签

    def forward(self,x,t):
        self.t = t 
        self.y = softmax(x)
        loss = cross_entropy_error(self.y,self.t)
        self.loss = loss 
        return loss 
    
    def backward(self,dout=1):
        batch_size = self.t.shape[0]
        if self.t.size == self.y.size:
            dx = (self.y-self.t)/batch_size
        else:
            dx = self.y.copy()
            dx[np.arange(batch_size),self.t] -= 1
            dx = dx/batch_size
        return dx 